/**
* Copyrights 2011 Michal Saman, m.saman@designplus.cz.
* This source code is release under the Apache License 2.0.
* http://www.apache.org/licenses/LICENSE-2.0
*
* @file	 mem.h
* @brief Memory space routines
*/

#include <env.h>
#include <errno.h>
#include <mem/mem.h>
#include <mem/vmu.h>
#include <hal/hal.h>

/* Global kernel stack descriptor */
stack_t	_kstack;

/**
 * Routine move current stack to new position, copies his content and switch SP, BP to point
 * to new stack.
 * We tries to find EBP pointers and realocate theirs values to point new stack corretly.
 * @param new_stack_addr		Memory addres of new stack (will be allocated by VMU)
 * @param old_stack_add			Memory addres of start of old stack
 * @param stack_size			Size of new stack
 */
int move_stack(void* new_stack_addr, void* old_stack_addr, uint32_t stack_size, stack_t* new_stack) {
	disable_interrupts();

	uint32_t i;

	//vmu_do_paging(false);

	// allocate space for new stack
	for (i= (uint32_t)new_stack_addr; i>=((uint32_t)new_stack_addr-stack_size); i -= PAGE_SIZE ) {
		if (vmu_alloc_page( vmu_get_page(i) ) != true) {
			// we don't have space to allocate whole stack
			stack_size = (uint32_t)(new_stack_addr-i);
			if (stack_size > 0)
				//kprintf("Warning: lack of memory to create new stack of required size,\nstack reduced to: %u kB.\n", stack_size/1024);
			break;
		}
	}

	//vmu_do_paging(true);

	kprintf("Stack size: %u\n", stack_size);

	if (stack_size==0) {
		return ENOMEM;
	}

	// fill stack structure
	new_stack->size = stack_size;
	new_stack->start_address = (uint32_t)new_stack_addr;

	// Current ESP, EBP
	uint32_t old_sp;
	uint32_t old_bp;
	__asm__ volatile ("mov %%esp, %0" : "=r"(old_sp));
	__asm__ volatile ("mov %%ebp, %0" : "=r"(old_bp));

	// Determine new ESP, EBP position
	uint32_t offset, new_sp, new_bp;
	offset = new_stack_addr - old_stack_addr;
	new_sp = old_sp + offset;
	new_bp = old_bp + offset;

	// Copy stack contents
	memcpy((void*)new_sp, (void*)old_sp, old_stack_addr - old_sp);

	//kprintf("Value at old_bp: 0x%x, value at new_bp: 0x%x\n", *(uint32_t*)old_bp, *(uint32_t*)new_bp );

	// change current EBP to point to new stack (add offset)
	// There is a problem that we can't 100% assume that value is EBP
	for (i=(uint32_t)new_stack_addr; i>((uint32_t)new_stack_addr-stack_size); i -=4) {
		uint32_t tmp = *(uint32_t*)i;
		if ( (old_sp < tmp) && (tmp < (uint32_t)old_stack_addr)) {
			tmp = tmp + offset;
			*((uint32_t*)i) = tmp;
		}
	}

	//kprintf("Value at old_bp: 0x%x, value at new_bp: 0x%x\n", *(uint32_t*)old_bp, *(uint32_t*)new_bp );

	// Change stack
	//kprintf("Switch ESP to 0x%x\n", new_sp);
	//kprintf("Switch EBP to 0x%x\n", new_bp);
	__asm__ volatile ("mov %0, %%esp" : : "r"(new_sp));
	__asm__ volatile ("mov %0, %%ebp" : : "r"(new_bp));

	enable_interrupts();

	return EOK;
}

/*
 * Clones source process memspace to destination process.
 * @param sp	Source process
 * @param dp	Destination process
 */
int clone_memspace(process_t *sp, process_t *dp) {
	struct page_directory *pd = vmu_clone_page_dir((struct page_directory*)sp->pcontext.page_dir);
	if (pd) {
		dp->pcontext.page_dir = pd;
		return EOK;
	} else {
		return EBADMEM;
	}
}
